//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
namespace LargoCommon.Music
{
using Abstract;
using JetBrains.Annotations;
using Localization;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
///
/// Musical Block Model.
///
[Serializable]
public sealed class HarmonicModel : AbstractModel, ICloneable
{
#region Fields
///
/// Harmonic motives.
///
[NonSerialized]
private IEnumerable harmonicMotives;
///
/// The harmonic bars
///
private IEnumerable harmonicBars;
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
public HarmonicModel() {
this.HarmonicMotives = new List();
}
#endregion
#region Public Properties
///
/// Gets the harmonic streams.
///
///
/// The harmonic streams.
///
[UsedImplicitly]
public IList HarmonicStreams {
get {
var list = new List();
var motives = this.HarmonicMotives;
foreach (var motive in motives) {
if (motive.Length <= 8) {
continue;
}
list.Add(motive.HarmonicStream);
}
return list;
}
}
///
/// Gets or sets the harmonic motives.
///
///
/// The harmonic motives.
///
public IEnumerable HarmonicMotives {
get {
Contract.Ensures(Contract.Result>() != null);
if (this.harmonicMotives == null) {
throw new InvalidOperationException("Harmonic motives are null.");
}
return this.harmonicMotives;
}
set => this.harmonicMotives = value ?? throw new ArgumentException(LocalizedMusic.String("Argument cannot be empty."), nameof(value));
}
///
/// Gets or sets the rhythmic structures.
///
/// Property description.
public IEnumerable HarmonicBars {
get {
Contract.Ensures(Contract.Result>() != null);
if (this.harmonicBars != null) {
return this.harmonicBars;
}
var rs = new List();
foreach (var m in this.HarmonicMotives) {
rs.AddRange(m.HarmonicStream.HarmonicBars);
}
this.harmonicBars = rs;
//// var rsd = (from r in rs select r).Distinct().OrderBy(x => x.Length).ToList(); ////. x.ElementSchema);
//// return rsd;
return rs;
}
set => this.harmonicBars = value;
}
#endregion
#region Other public properties
///
/// Gets the first melodic bar.
///
/// Property description.
[UsedImplicitly]
public int FirstMelodicBar {
get {
var barNumber = (from c in this.BlockChanges.Changes where c.IsMelodicalNature orderby c.BarNumber select c.BarNumber).FirstOrDefault();
return barNumber;
}
}
#endregion
#region Static factory methods
///
/// Extracts the musical block model.
///
/// The musical block.
///
/// Returns value.
///
public static HarmonicModel GetNewModel(MusicalBlock musicalBlock) {
musicalBlock.Header.NumberOfLines = (byte)musicalBlock.Strip.Lines.Count;
var model = GetNewModel(musicalBlock.Header.Name, musicalBlock);
model.Number = musicalBlock.Header.Number;
model.SourceMusicalBlock = musicalBlock;
//// model.Core = new MusicalCore(musicalBlock.Header);
model.Header = musicalBlock.Header;
ProcessLogger.Singleton.SendLogEvent(null, LocalizedMusic.String("Analyzing tempo..."), 0); //// musicalBlock.Name
var tempoChanges = musicalBlock.AnalyzeTempoChanges();
tempoChanges.ForAll(change => model.BlockChanges.Changes.Add(change));
ProcessLogger.Singleton.SendLogEvent(null, LocalizedMusic.String("Analyzing harmony..."), 0); //// musicalBlock.Name
var harmonicAnalysis = MusicalSettings.Singleton.HarmonicAnalysis; //// HarmonicAnalyzeType.DivisionByTicks
var harmonicAnalyzer = new HarmonicAnalyzer(musicalBlock.Body);
var harmonicChanges = harmonicAnalyzer.AnalyzeHarmony(model, harmonicAnalysis);
harmonicChanges.ForAll(change => model.BlockChanges.Changes.Add(change));
//// 2016 this.Body.MakeBars(model.Header.NumberOfBars, this.Header); model.SourceMusicalBlock.NumberOfBars
//// 2019/12 !? musicalBlock.Body.SetHarmonicBasis(model.BlockChanges); //// true
return model;
}
///
/// Gets the new musical model.
///
/// Name of the model.
/// The musical block.
///
/// Returns value.
///
/// Null Exception.
public static HarmonicModel GetNewModel(string modelName, MusicalBlock musicalBlock) {
Contract.Requires(musicalBlock != null);
var model = new HarmonicModel {
Name = modelName,
IsSelected = false
};
if (model == null) {
throw new ArgumentNullException(nameof(modelName));
}
model.Header = musicalBlock.Header;
model.SourceMusicalBlock = musicalBlock;
return model;
}
#endregion
#region Public methods
///
/// Rhythmic of harmony.
///
/// Returns value.
[UsedImplicitly]
public IList RhythmicOfHarmony() {
var structs = new List();
var bars = this.HarmonicBars;
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var harBar in bars) {
var rstruct = harBar.RhythmicStructure; //// Clone?
structs.Add(rstruct);
}
return structs;
}
///
/// Gets the harmonic motive.
///
/// The number.
/// Returns value.
[UsedImplicitly]
public HarmonicMotive GetHarmonicMotive(int number) {
var cnt = this.HarmonicMotives.Count();
if (cnt == 0) {
return null;
}
var localNumber = number;
if (localNumber > cnt) {
checked {
localNumber = ((number - 1) % cnt) + 1;
}
}
//// HarmonicMotive motive = this.HarmonicMotives.ElementAt(localNumber);
var motive = (from m in this.HarmonicMotives where m.Number == localNumber select m).FirstOrDefault();
return motive;
}
///
/// Adds the motive.
///
/// The motive.
public void AddMotive(HarmonicMotive motive) {
Contract.Requires(motive != null);
//// motive.Core = this;
motive.Recompute();
((List)this.HarmonicMotives).Add(motive);
}
///
/// Returns a that represents this instance.
///
///
/// A that represents this instance.
///
[UsedImplicitly]
public override string ToString() {
var system = this.Header.System;
return system.HarmonicOrder.ToString(CultureInfo.InvariantCulture);
}
#endregion
/// Makes a deep copy of the BlockModel object.
/// Returns object.
public object Clone() {
var model = new HarmonicModel {
Name = this.Name,
Number = this.Number,
//// Core = this.Core,
Header = (MusicalHeader)this.Header.Clone(),
BlockChanges = new MusicalChanges()
};
model.BlockChanges.AppendCopyOfChanges(this.BlockChanges.Changes); //// model
model.SourceMusicalBlock = this.SourceMusicalBlock;
//// model.MakeBars(model.NumberOfBars);
return model;
}
#region Material Extractor
///
/// Extract harmonic material.
///
///
/// Returns value.
///
public HarmonicMaterial ExtractHarmonicMaterial() {
var system = this.Header.System;
var thm = new HarmonicMaterial { Name = this.Name, HarmonicOrder = system.HarmonicOrder };
//// dcm.AddToTHarmonicMaterial(thm);
var list = this.ListOfStructures();
var groupList = (from ms in list
group ms by ms.GetStructuralCode into g
select g).ToList();
groupList.ForEach(g => {
var hs = g.FirstOrDefault();
if (hs == null) {
return;
}
var mhs = hs; //// clone?
var chs = hs.GetClassStructure;
hs.ClassCode = chs.GetStructuralCode;
hs.Occurrence = g.Count();
thm.Structures.Add(mhs);
});
//// dcm.SaveChanges();
return thm;
}
#endregion
///
/// Lists the of structures.
///
///
/// Returns value.
///
private IEnumerable ListOfStructures() {
var list = new Collection();
foreach (var structure in from motive in this.HarmonicMotives
from bar in motive.HarmonicStream.HarmonicBars
from structure in bar.HarmonicStructures
let sc = structure.GetStructuralCode
where !string.IsNullOrEmpty(sc)
select structure) {
list.Add(structure);
}
return list;
}
}
}